﻿
namespace Octopus.RemoteHpcPS

open System
open System.IO
open System.Net
open System.Net.Sockets
open System.ServiceProcess



type HpcPsService =
  inherit System.ServiceProcess.ServiceBase

  val private listener : TcpListener
  val private components : System.ComponentModel.IContainer
  val private serviceBinPath : string

  new() as x =
    {
      listener = new TcpListener(IPAddress.Any, 17500)
      components = new System.ComponentModel.Container()
      serviceBinPath = let s = typeof<HpcPsInstaller>.Assembly.Location in s.Substring(0, s.LastIndexOf(@"\"))
    }
    then
      x.ServiceName <- "Octopus Remote HPC PowerShell"

  //--

  member private x.WriteInLog message =
    let logpath = x.serviceBinPath + @"\log.txt"
    use fs = new FileStream(logpath, FileMode.Append)
    use sw = new StreamWriter(fs)
    sw.WriteLine("[{0}] {1}", System.DateTime.Now.ToString(), message)
    sw.WriteLine()
    sw.Close()    

  //--
  
  member private x.ManageAcceptedClient (client:TcpClient) =
    // Open the socket stream
    // Note: don't declate these with "use" or the objects will be disposed at
    // the end of the block!
    // I need it alive inside the Async block (stream are closed inside there)
    let stream = client.GetStream()
    let sr = new StreamReader(stream)
    let sw = new StreamWriter(stream)
    sw.AutoFlush <- true
    
    let endpoint = client.Client.RemoteEndPoint :?> IPEndPoint
    let clientip = endpoint.Address
      
    let funcToCall =
      // Read the command to execute
      let functionName = sr.ReadLine()
      match functionName with
      | "getNodeState" -> RFunctions.getNodeState
      | "setNodeOnline" -> RFunctions.setNodeOnline
      | "setNodeOffline" -> RFunctions.setNodeOffline
      | "assign_HpcNodeTemplate" -> RFunctions.assign_HpcNodeTemplate
      | "import_HpcNodeXml" -> RFunctions.import_HpcNodeXml
      | "instanceHpcXmlNodeImport" -> RFunctions.instanceHpcXmlNodeImport
       
      | _ -> fun _ sw -> sw.WriteLine(false); sw.WriteLine("Unknown function: " + functionName)
      
    try
      try funcToCall sr sw
      with e ->
        sw.WriteLine(false);
        sw.WriteLine(e.Message)
        sw.WriteLine(e.StackTrace)
        let msg = "in ManageAcceptedClient: " + e.Message + "\r\n" + e.StackTrace
        x.WriteInLog msg
    finally
      client.Close()
  
  member private x.AcceptTcpClientCallback (ar:IAsyncResult) =
    try
      let client = x.listener.EndAcceptTcpClient(ar)
      Async.Start(
        async {
          try x.ManageAcceptedClient client
          with e -> let msg = "in AcceptTcpClientCallback(1): " + e.Message + "\r\n" + e.StackTrace in x.WriteInLog msg
        }
      )
    with e ->
      let msg = "in AcceptTcpClientCallback(1): " + e.Message + "\r\n" + e.StackTrace
      x.WriteInLog msg

    try
      x.listener.BeginAcceptTcpClient(new AsyncCallback(x.AcceptTcpClientCallback), null) |> ignore
    with e ->
      let msg = "in AcceptTcpClientCallback(2): " + e.Message + "\r\n" + e.StackTrace
      x.WriteInLog msg
    ()

  //--     

  override x.OnStart (args:string array) =
    let logpath = x.serviceBinPath + @"\log.txt"
    let fs = new FileStream(logpath, FileMode.Append)
    let sw = new StreamWriter(fs, AutoFlush=true)
    sw.WriteLine("-Logging started at {0}", System.DateTime.Now.ToString())
    sw.WriteLine("Trying to start the service...")
    
    try
      try
        //------------------------------------------------------
        sw.Write("  Starting the tcp listener... ")
        x.listener.Start()
        x.listener.BeginAcceptTcpClient(new AsyncCallback(x.AcceptTcpClientCallback), null) |> ignore
        sw.WriteLine("done.")
        //------------------------------------------------------
        sw.WriteLine("Service started!")
      with e ->
        sw.WriteLine()
        sw.WriteLine("Exception: " + e.Message)
        sw.WriteLine(e.StackTrace)
        reraise()
    finally
      sw.WriteLine("-Log end-----------------------------------------------------------\n")
      sw.WriteLine()
      sw.Close()

  override x.OnStop() =
    let logpath = x.serviceBinPath + @"\log.txt"
    let fs = new FileStream(logpath, FileMode.Append)
    let sw = new StreamWriter(fs, AutoFlush=true)
    sw.WriteLine("-Logging started at {0}", System.DateTime.Now.ToString())
    sw.WriteLine("Stopping the service... ")

    try
      sw.Write("  stopping the listener...")
      x.listener.Stop()
      sw.WriteLine(" done.")
    with e ->
      sw.WriteLine()
      sw.WriteLine(e.Message)
      sw.WriteLine(e.StackTrace)

    sw.WriteLine("done.")
    sw.WriteLine("-Log end-----------------------------------------------------------\n")
    sw.WriteLine()
    sw.Close()


  //member x.OnDebugStart (args:string array) = x.OnStart(args)
  //member x.OnDebugStop () = x.OnStop()
    


//
//-------------------------------------------------------------
//

module Main =
  let servicesToRun : ServiceBase array = [| new HpcPsService() |]
  ServiceBase.Run(servicesToRun)

